import { MciMessageBox } from '@/components/base';
import { TOKEN_STORAGE_KEY } from '@/constants/config';
// import { useKc } from '@/keycloak';
import { ElNotification } from 'element-plus';
import { isNotEmpty } from 'microconnect-common/utils/is';
import { hasProtocol, toUrl } from 'microconnect-common/utils/url-helper';
import conf from '../config';
import { FetchCache } from './fetch-helper';
import storage from './storage';
import { createParser } from 'eventsource-parser';

enum HttpMethod {
  get = 'GET',
  post = 'POST',
  put = 'PUT',
  patch = 'PATCH',
  delete = 'DELETE'
}
const fetchCache = new FetchCache();

// 自定义错误信息code
const errorWhitelistCodes: string[] = ['0001'];

export interface IRequestInit extends RequestInit {
  /** 会被拼接在 url 上的参数 */
  params?: IRecordValue<any>;
  /** body 参数 */
  body?: any;
  /** restful url参数 */
  paramNames?: string[];
  /** 是否返回所有数据 */
  isAll?: boolean;
  /** 是否需要token */
  noToken?: boolean;
  /** 指定接口 */
  domain?: string;
}
export const post = <T>(url: string, conf?: IRequestInit) => {
  return fetchFn<T>(url, Object.assign({ method: HttpMethod.post }, conf));
};
export const get = <T>(url: string, conf?: IRequestInit) => {
  return fetchFn<T>(url, Object.assign({ method: HttpMethod.get }, conf));
};

// const { logout, isNeedLogout } = useKc();

export const fetchFn = async <TResponseData>(url: string, opts: IRequestInit) => {
  let { body = {}, method = 'POST' } = opts;

  let requestUrl = url;
  if (url && !hasProtocol(url)) {
    requestUrl = (opts.domain ?? conf.apiDomain) + url;
  }

  const controller = new AbortController();
  const { signal } = controller;

  const config: RequestInit = {
    method,
    signal,
    headers: {
      'Content-Type': 'application/json'
    }
  };

  return new Promise<any>(async (resolve, reject) => {
    try {
      // if (isNeedLogout.value) return;

      const token = storage.get(TOKEN_STORAGE_KEY);
      if (token && opts.noToken !== true) {
        (config.headers as any)['Authorization'] = 'Bearer ' + token;
      }
      if (method === 'GET') {
        requestUrl += isNotEmpty(body) ? '?' + toUrl(body) : '';
        opts.body = {};
      } else {
        if (isNotEmpty(body)) {
          config.body = JSON.stringify(body);
        }
      }

      if (opts.headers) Object.assign(config.headers!, opts.headers);

      const fetchRes = await fetch(requestUrl, config);
      const res = await fetchRes.json();

      if (fetchRes.status === 401) {
        MciMessageBox.confirm('登录状态已过期，请重新登录', '提示', {
          showCancelButton: false,
          showClose: false,
          closeOnClickModal: false,
          confirmButtonText: '重新登录'
        }).then(() => {
          logout();
        });
        // isNeedLogout.value = true;
      }

      if ((fetchRes.status === 200 && res.code === 403) || (fetchRes.status === 200 && res.data?.userId && !res.data?.hasPermission)) {
        // TODO 无权限
        // isNeedLogout.value = true;
      }

      fetchCache.add(requestUrl, config, { resolve, reject }, controller);
      const request = fetchCache.find(requestUrl, config);

      if (opts.isAll) {
        request?.callbackList.map((cb) => cb.resolve(res));
      } else {
        if (res.code !== 0) {
          // 特殊 code 处理
          if (errorWhitelistCodes.includes(res.code)) {
            ElNotification.error(res.data?.[0]?.message);
            return reject(res);
          }
          const isServerError = [res.code, res.status].some((code) => Number(code) === 500);
          const otherMsg = res.localizedMessage || res.message || res.msg || res.error;
          // 系统服务报错统一友好提示
          const message = isServerError ? '系统维护中，请稍后重试' : otherMsg;

          ElNotification.error(message);
          request?.callbackList.map((cb) => cb.reject(new Error(res.code + res.message)));
        } else {
          request?.callbackList.map((cb) => cb.resolve(res.data as TResponseData));
        }
      }
      fetchCache.remove(request);
    } catch (error) {
      if (String(error).includes('aborted') === true) return;
      reject(error);
    }
  });
};

export async function* streamAsyncIterable(stream: ReadableStream) {
  const reader = stream.getReader();
  try {
    while (true) {
      const { done, value } = await reader.read();
      if (done) {
        return;
      }
      yield value;
    }
  } finally {
    reader.releaseLock();
  }
}

export const fetchOfSSE = async (url: string, options: IRequestInit & { onMessage: (message: string) => void }) => {
  const { onMessage, ...fetchOptions } = options;
  let requestUrl = url;
  if (url && !hasProtocol(url)) {
    requestUrl = conf.apiDomain + url;
  }
  const token = storage.get(TOKEN_STORAGE_KEY);
  if (token && options.noToken !== true) {
    (options.headers as any)['Authorization'] = 'Bearer ' + token;
  }
  const resp = await fetch(requestUrl, fetchOptions);
  if (!resp.ok) {
    const error = await resp.json().catch(() => null);
    throw new Error(!error ? JSON.stringify(error) : `${resp.status} ${resp.statusText}`);
  }
  const parser = createParser((event) => {
    if (event.type === 'event') {
      onMessage(event.data);
    }
  });
  for await (const chunk of streamAsyncIterable(resp.body!)) {
    const str = new TextDecoder().decode(chunk);
    parser.feed(str);
  }
};
